#include "util.hpp"
#include <cmath> // for fabs()
#include <iostream>
#include <vector>
#include <algorithm>  // for std::find in hasEntry()

namespace hms
{


bool approxEqual(scalar a, scalar b)
{
	return fabs(a - b) <=
		( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * hms::epsilon);
}


void printProgress(
	Index min,
	Index current,
	Index max
){
	if ( current == min )
		std::cout << "    ";
	static long long int progress {0};
	auto temp { 100 * current / max };
	if ( temp != progress )
	{
		progress = temp;
		std::cout << ( 
			progress < 10 ? "\b\b" : (
			progress < 100 ? "\b\b\b" : "\b\b\b\b" )
		) << progress << "%";
	}
	if ( current == max )
		std::cout << "\n";
}


bool sameEntries(
	const Ref<const ArrayXi>& a,
	const Ref<const ArrayXi>& b
){
	if ( a.size() != b.size() ) return false;
	
	if ( (a == b).all() ) return true;
	
	
	for ( Index entry : a )
	{
		if ( std::find( b.cbegin(), b.cend(), entry ) == b.cend() )
			return false;
	}
	return true;
}

bool sameEntries(
	const Ref<const ArrayXi>& a,
	const IdxVector& b
){
	if ( static_cast<uIndex>( a.size() ) != b.size()) return false;
	
	for ( Index entry : a )
		if ( std::find( b.cbegin(), b.cend(), entry ) == b.cend() )
			return false;
	return true;
}

bool sameEntries(
	const IdxVector& a,
	const Ref<const ArrayXi>& b
){
	return sameEntries( b, a );
}


std::optional<Array2i> overlap(Index aBeg, Index aEnd, Index bBeg, Index bEnd){
	Index beg { std::max(aBeg, bBeg) };
	Index end { std::min(aEnd, bEnd) };
	/* check whether there is actual overlap */
	if ( beg > aEnd || end < aBeg ) /* equivalent to beg > bEnd || end < bBeg */
		return {};
	else
		return Array2i{ beg, end };
}

std::optional<Array2i> overlap(
	const Ref<const Array2i>& a,
	const Ref<const Array2i>& b
){
	return overlap( a[0], a[1], b[0], b[1] );
}

std::optional<Array2i> overlap_usingSizes(
	Index aBeg, Index aSize, Index bBeg, Index bSize
){
	return overlap( aBeg, aBeg+aSize-1, bBeg, bBeg+bSize-1 );
}

std::string ltrim(const std::string& s) {
    return std::regex_replace(s, std::regex("^\\s+"), std::string(""));
}

std::string rtrim(const std::string& s) {
    return std::regex_replace(s, std::regex("\\s+$"), std::string(""));
}

std::string trim(const std::string& s) {
    return ltrim(rtrim(s));
}


fs::path subdirOrAbsolute( const fs::path& wd, const std::string& pathStr ){
	fs::path absPath {wd};

	if ( !pathStr.empty() ){
		if ( fs::path { pathStr }.is_relative() )
			absPath /= pathStr;
		else
			absPath = { std::move(pathStr) };
	}
	return fs::canonical(absPath);
}

/**
 * @brief Convert an angle from degrees to radians
 * 
 * @param angle is the angle in degrees
 * @return scalar 
 */
scalar degToRad(scalar angle){
	return angle * math::pi / 180;
}


} // end namespace hms